using System;
using System.Collections.Generic;
using System.Text;
using NHibernate;

namespace Lib
{
    public static class Persistor
    {
        private static NHibernate.Cfg.Configuration _cfg;
        private static NHibernate.ISessionFactory _sessionFactory;
        private const string KEY_NHIBERNATE_SESSION = "LAURIERLOOP_NHIBERNATE_SESSION";
        private static Type _sampleType;


        private static bool _configuration_is_set = false;
        public static bool IsConfigurationSet
        {
            get { return _configuration_is_set; }
        }


        private static NHibernate.ISession _currentSession;
        public static bool IsSessionCreated()
        {
            return _currentSession != null;

        }

        
        public static DatabaseType CurrentDatabaseType = DatabaseType.MSSQL2005;

        public enum DatabaseType
        {
            MySQL5, MSSQL2000, MSSQL2005, Postgre, Oracle9i
        }

        public static void SetDatabaseType(DatabaseType chosenDatabaseType)
        {
            CurrentDatabaseType = chosenDatabaseType;
        }

        private static void configureHibernate(String connectionString)
        {
            _cfg = new NHibernate.Cfg.Configuration();


            switch (CurrentDatabaseType)
            {
                case DatabaseType.MSSQL2005:
                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionProvider] =
                "NHibernate.Connection.DriverConnectionProvider";
                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionDriver] =
                "NHibernate.Driver.SqlClientDriver";
                    _cfg.Properties[NHibernate.Cfg.Environment.Dialect] =
                "NHibernate.Dialect.MsSql2005Dialect";

                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionString] = connectionString;
                    break;

                case DatabaseType.MSSQL2000:
                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionProvider] =
                "NHibernate.Connection.DriverConnectionProvider";
                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionDriver] =
                "NHibernate.Driver.SqlClientDriver";
                    _cfg.Properties[NHibernate.Cfg.Environment.Dialect] =
                "NHibernate.Dialect.MsSql2000Dialect";

                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionString] = connectionString;
                    break;

                case DatabaseType.MySQL5:
                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionProvider] =
                "NHibernate.Connection.DriverConnectionProvider";
                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionDriver] =
                "NHibernate.Driver.MySqlDataDriver";
                    _cfg.Properties[NHibernate.Cfg.Environment.Dialect] =
                "NHibernate.Dialect.MySQLDialect";

                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionString] = connectionString;
                    break;

                case DatabaseType.Postgre:

                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionProvider] =
                "NHibernate.Connection.DriverConnectionProvider";
                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionDriver] =
                "NHibernate.Driver.NpgsqlDriver";
                    _cfg.Properties[NHibernate.Cfg.Environment.Dialect] =
                "NHibernate.Dialect.PostgreSQLDialect";

                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionString] = connectionString;

                    break;

                case DatabaseType.Oracle9i:

                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionProvider] = new NHibernate.Connection.DriverConnectionProvider();
                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionDriver] = new NHibernate.Driver.NpgsqlDriver();
                    _cfg.Properties[NHibernate.Cfg.Environment.Dialect] = new NHibernate.Dialect.Oracle9Dialect(); //to be checked when we switch to Oracle
                    _cfg.Properties[NHibernate.Cfg.Environment.ConnectionString] = connectionString;

                    break;

                default:
                    throw new Exception("No Database Type Configured");
            }

            _cfg.Properties[NHibernate.Cfg.Environment.ConnectionString] =
                connectionString;
        }

        public static void Configure(String connectionString, Type sampleType)
        {
            // we do this to be able to sync the connection string for the utilities
            configureHibernate(connectionString);
            setupMappingFromObjectTypeInDLL(sampleType);
            _configuration_is_set = true;
            _sessionFactory = _cfg.BuildSessionFactory();
        }

        public static void Configure(String connectionString, Type sampleType, DatabaseType chosenDatabaseType)
        {
            // we do this to be able to sync the connection string for the utilities
            SetDatabaseType(chosenDatabaseType);
            Configure(connectionString, sampleType);
        }

        public static ISession CurrentSession
        {
            get
            {
                if (System.Web.HttpContext.Current == null)
                {
                    // running without an HttpContext (non-web mode) 
                    // the nhibernate session is a singleton in the app domain 
                    if (_currentSession == null)
                    {
                        _currentSession = GetNewSession();
                    }
                    return _currentSession;
                }
                else
                {
                    // running inside of an HttpContext (web mode) 
                    // the nhibernate session is a singleton to the http request 
                    System.Web.HttpContext currentContext = System.Web.HttpContext.Current;

                    _currentSession = currentContext.Items[KEY_NHIBERNATE_SESSION] as ISession;

                    if (_currentSession == null)
                    {
                        _currentSession = GetNewSession();
                        currentContext.Items[KEY_NHIBERNATE_SESSION] = _currentSession;
                    }
                    return _currentSession;
                }
            }
        }

        public static ISession GetNewSession()
        {
            if (!(_configuration_is_set))
            {
                throw new Exception("Tried to get a session before configuring NHibernate.");
            }
            if (_sessionFactory == null)
            {
                throw new Exception("Tried to get a session without started SessionFactory.");
            }

            return _sessionFactory.OpenSession(new NHibernate.EmptyInterceptor());
        }

        private static void setupMappingFromObjectTypeInDLL(Type sampleType)
        {
            _sampleType = sampleType;
            NHibernate.Mapping.Attributes.HbmSerializer.Default.Validate = true;

            System.IO.MemoryStream stream = NHibernate.Mapping.Attributes.HbmSerializer.Default.Serialize(
                System.Reflection.Assembly.GetAssembly(sampleType));
            _cfg.AddInputStream((stream));
        }

        public static string OutputSchema() 
        {
            string[] outputSchemaList;
            string outputSchemaBlock = "";

            NHibernate.Dialect.MsSql2000Dialect DBDialect = new NHibernate.Dialect.MsSql2000Dialect();
            outputSchemaList = _cfg.GenerateSchemaCreationScript(DBDialect);

            foreach (string s in outputSchemaList)
            {
                outputSchemaBlock += (s + ";\n");
            }

            return outputSchemaBlock;
        }

    }
}
